﻿// <copyright file="XMLDataFileParser.cs" company="Oleg V. Polikarpotchkin">
// Copyright © 2008-2009 Oleg V. Polikarpotchkin. All Right Reserved
// </copyright>
// <author>Oleg V. Polikarpotchkin</author>
// <email>ov-p@yandex.ru</email>
// <date>2009-03-09</date>
// <summary>OpenWPFChart library. XML Data File Parser.</summary>
// <revision>$Id$</revision>

using System;
using System.Collections; // For IList
using System.Collections.Generic;
using System.Collections.ObjectModel; // For ObservableCollection
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Resources;
using OpenWPFChart.Helpers.XmlItemDataReader;
using OpenWPFChart.Parts;

namespace BasicSample
{
	/// <summary>
	/// XML Data File Parser
	/// </summary>
	static class XMLDataFileParser
	{
		/// <summary>
		/// Parses the <paramref name="fileName"/> file.
		/// </summary>
		/// <param name="fileName">Name of the file.</param>
		/// <returns><see cref="ItemData"/> collection from the <paramref name="fileName"/> file.</returns>
		/// <remarks>
		/// See XML data format description in SampleDataFiles/XmlFileFormat.txt. Details specific for
		/// this project see in the project Readme.txt file.
		/// </remarks>
		public static ObservableCollection<ItemData> Parse(string fileName)
		{
			// XML Data Mapping resource name.
			const string mappingResourceName = "xmldatamappings.xml";
			// Get xml Data Mapping stream from the "mappingResourceName" resource.
			Assembly assembly = Assembly.GetAssembly(typeof(XMLDataFileParser));
			ResourceManager rm = new ResourceManager(assembly.GetName().Name + ".g", assembly);
			IEnumerable<DataPointSeries> seriesCollection = null;
			using (TextReader mappingReader = new StreamReader(rm.GetStream(mappingResourceName)))
			using (TextReader dataReader = new StreamReader(File.OpenRead(fileName)))
			{
				seriesCollection = Loader.Load(dataReader, mappingReader);
			}

			ObservableCollection<ItemData> items = new ObservableCollection<ItemData>();
			foreach (DataPointSeries series in seriesCollection)
			{
				if (!string.IsNullOrEmpty(series.ItemType) && series.ItemType != "SampledCurve"
					&& series.ItemType != "ScatteredPoints")
					continue;

				// Get OpenWPFChart.Parts.DataPoint<...> constructor.
				ConstructorInfo pointCtorInfo = GetTypedPointConstructorInfo(series);
				Debug.Assert(pointCtorInfo != null, "pointCtorInfo != null");
				// Create OpenWPFChart.Parts.DataPoint<...> collection.
				IList points = CreateTypedPointCollectionInstance(series);
				Debug.Assert(points != null, "points != null");
				// Fill point collection.
				foreach (object[] pt in series.GetPoints())
				{
					object typedDataPoint = pointCtorInfo.Invoke(pt);
					points.Add(typedDataPoint);
				}

				// Create ItemData object.
				string typeName = null;
				if (series.ItemType == "ScatteredPoints")
				{
					if (series.Dimensions.Length == 2)
						typeName = string.Format("OpenWPFChart.Parts.ScatteredPointsData`2[{0},{1}],OpenWPFChart.Parts"
							, series.Dimensions[0].ClrType.FullName
							, series.Dimensions[1].ClrType.FullName);
					else if (series.Dimensions.Length == 3)
						typeName = string.Format("OpenWPFChart.Parts.ScatteredPointsData`3[{0},{1},{2}],OpenWPFChart.Parts"
							, series.Dimensions[0].ClrType.FullName
							, series.Dimensions[1].ClrType.FullName
							, series.Dimensions[2].ClrType.FullName);
				}
				else
					typeName = string.Format("OpenWPFChart.Parts.SampledCurveData`2[{0},{1}],OpenWPFChart.Parts"
						, series.Dimensions[0].ClrType.FullName
						, series.Dimensions[1].ClrType.FullName);
				if (typeName == null)
					continue; // Don't knows what is the ItemData type.

				Type type = Type.GetType(typeName);
				ConstructorInfo ctorInfo = type.GetConstructor(new Type[0]);
				ItemData itemData = ctorInfo.Invoke(null) as ItemData;
				itemData.ItemName = series.Dimensions[1].Name;
				PropertyInfo pointsInfo = type.GetProperty("Points");
				pointsInfo.SetValue(itemData, points, null);

				items.Add(itemData);
			}
			return items;
		}

		/// <summary>
		/// Gets OpenWPFChart.Parts.DataPoint&lt;...&gt; constructor.
		/// </summary>
		/// <param name="series">The series.</param>
		/// <returns></returns>
		static ConstructorInfo GetTypedPointConstructorInfo(DataPointSeries series)
		{
			if (series.Dimensions.Length == 2)
			{
				string typeName = string.Format("OpenWPFChart.Parts.DataPoint`2[{0},{1}],OpenWPFChart.Parts"
					, series.Dimensions[0].ClrType.FullName
					, series.Dimensions[1].ClrType.FullName);
				Type type = Type.GetType(typeName);
				Debug.Assert(type != null, "type != null", "Typed Point type not found.");
				return type.GetConstructor(new Type[] { series.Dimensions[0].ClrType, series.Dimensions[1].ClrType });
			}
			else if (series.Dimensions.Length == 3)
			{
				string typeName = string.Format("OpenWPFChart.Parts.DataPoint`3[{0},{1},{2}],OpenWPFChart.Parts"
					, series.Dimensions[0].ClrType.FullName
					, series.Dimensions[1].ClrType.FullName
					, series.Dimensions[2].ClrType.FullName);
				Type type = Type.GetType(typeName);
				Debug.Assert(type != null, "type != null", "Typed Point type not found.");
				return type.GetConstructor(new Type[] { series.Dimensions[0].ClrType
					, series.Dimensions[1].ClrType, series.Dimensions[2].ClrType });
			}
			return null;
		}

		/// <summary>
		/// Creates the Typed point collection.
		/// </summary>
		/// <param name="series">The series.</param>
		/// <returns></returns>
		static IList CreateTypedPointCollectionInstance(DataPointSeries series)
		{
			string typeName = null;
			if (series.Dimensions.Length == 2)
				typeName = string.Format("System.Collections.Generic.List`1"
					+ "[[OpenWPFChart.Parts.DataPoint`2[{0},{1}],OpenWPFChart.Parts]]"
					, series.Dimensions[0].ClrType.FullName, series.Dimensions[1].ClrType.FullName);
			else if (series.Dimensions.Length == 3)
				typeName = string.Format("System.Collections.Generic.List`1"
					+ "[[OpenWPFChart.Parts.DataPoint`3[{0},{1},{2}],OpenWPFChart.Parts]]"
					, series.Dimensions[0].ClrType.FullName
					, series.Dimensions[1].ClrType.FullName
					, series.Dimensions[2].ClrType.FullName);
			if (typeName == null)
				return null;

			Type type = Type.GetType(typeName, true);
			Debug.Assert(type != null, "type != null", "Typed Point Collection type not found.");
			ConstructorInfo ctorInfo = type.GetConstructor(new Type[0]);
			Debug.Assert(ctorInfo != null, "ctorInfo != null");
			return ctorInfo.Invoke(null) as IList;
		}
	}
}
